﻿
package extremefx.ui {
	import extremefx.drawing.SlicedBitmap;
	import extremefx.events.EventHandler;
	import extremefx.events.MouseEventArgs;
	import extremefx.geom.Size;
	import extremefx.ui.UIComponent;
	import extremefx.ui.styles.UIStates;
	
	import flash.display.BitmapData;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;	

	/**
	 * @author Marcelo Volmaro
	 */
	internal class ScrollBar extends UIComponent {
		private var _position:int;
		private var _trackSize:int;
		private var _minThumbPos:int;
		private var _minThumbSize:int;
		private var _maxThumbPos:int;
		
		private var _lastY:int;
		
		private var _thumbSize:int;
		private var _tMax:int;
		
		private var _mWheeList:Boolean;
		
		private var _trackHolder:Sprite;
		private var _arrow1Holder:Sprite;
		private var _arrow2Holder:Sprite;
		private var _arrow2Pos:Point;
		private var _arrow2Size:Size;
		private var _thumbHolder:Sprite;
		
		private var _scrollable:Boolean;
		private var _scrolling:Boolean;

		private var _baseSize:int;
		private var _scrID:uint;
		
		private var _trackHandler:ButtonSprite;
		private var _thumbHandler:ButtonSprite;
		private var _arrow1Handler:ButtonSprite;
		private var _arrow2Handler:ButtonSprite;
		
		protected var _increment:int;
		protected var _wSize:int;
		protected var _minPos:int;
		protected var _maxPos:int;
		
		protected var __pad1:String;//[top] vertical, [left] horizontal
		protected var __pad2:String;//[bottom] vertical, [right] horizontal
		protected var __pad3:String;//[left] vertical, [top] horizontal
		protected var __size1:String;//[width] vertical, [height] horizontal
		protected var __size2:String;//[height] vertical, [width] horizontal
		protected var __pos1:String;//[x] vertical, [y] horizontal
		protected var __pos2:String;//[y] vertical, [x] horizontal
		protected var __mpos1:String;//[mouseY] vertical, [mouseX] horizontal
		protected var __mpos2:String;//[mouseX] vertical, [mouseY] horizontal
		
		public var useMouseWheel:Boolean;
		
		public function ScrollBar(pStyle:String, pParameters:UIParameters = null) {
			super();
			_focusVisual = false;
			
			initStyle(pStyle, pParameters);
			
			var s:Sprite = _trackHolder = new Sprite();
			s.useHandCursor = false;
			s[__pos2] = _style.padding[__pad1];
			_visualUI.addChild(s);
			
			var mh:ButtonSprite = _trackHandler = new ButtonSprite(s);
			_hookArrow(mh, _startTrackScroller, false);
			
			s = _thumbHolder = new Sprite();
			s.useHandCursor = false;
			_visualUI.addChild(s);
			mh = _thumbHandler = new ButtonSprite(s);
			mh.onPress.add(_startDragThumb);
			
			mh.onRelease.add(_stopDragThumb);
			mh.onReleaseOutside.add(_stopDragThumb);
			
			mh.onRollOver.add(_rollOver);
			mh.onRollOut.add(_rollOut);
			mh.onPress.add(_press);
			mh.onRelease.add(_release);
			
			_minThumbPos = _style.padding[__pad1];
			_thumbHolder[__pos1] = _style.padding[__pad3];
			
			s = _arrow1Holder = new Sprite();
			s.useHandCursor = false;
			mh = _arrow1Handler = new ButtonSprite(s);
			_hookArrow(mh, _start1Scroller);
			
			_arrow1Holder.x = parseInt(_style.getRawProperty("arrow1PositionLeft")) || 0;
			_arrow1Holder.y = parseInt(_style.getRawProperty("arrow1PositionTop")) || 0;
			_visualUI.addChild(s);
			
			s = _arrow2Holder = new Sprite();
			s.useHandCursor = false;
			mh = _arrow2Handler = new ButtonSprite(s);
			_hookArrow(mh, _start2Scroller);
			
			_visualUI.addChild(s);
			
			_arrow2Pos = new Point(parseInt(_style.getRawProperty("arrow2PositionLeft")) || 0, parseInt(_style.getRawProperty("arrow2PositionTop")) || 0);
			_minThumbSize = parseInt(_style.getRawProperty("minThumbSize")) || 8;
			
			var bd:BitmapData = _style.getImage(1,"arrow2ImageUrl").originalBitmap;
			_arrow2Size = new Size(bd.width, bd.height);
			
			useMouseWheel = true;
			_increment = 1;
			
			_baseSize = this["_"+__size1] = _style.getFirstBMPData()[__size1];
		}
		
		private function _hookArrow(pBS:ButtonSprite, pStart:Function, pAttachToDrag:Boolean = true):void {
			pBS.onPress.add(pStart);
			if (pAttachToDrag) pBS.onDragOver.add(pStart);
			
			pBS.onRelease.add(_stopScrolling);

			pBS.onRollOut.add(_stopScrolling);
			pBS.onDragOut.add(_stopScrolling);
			
			pBS.onRollOver.add(_rollOver);
			pBS.onDragOver.add(_press);
			
			pBS.onRollOut.add(_rollOut);
			pBS.onDragOut.add(_rollOut);
			
			pBS.onPress.add(_press);
			pBS.onRelease.add(_release);
		}
		
		override public function set size(pSize:Size):void {
			_trackSize = pSize[__size2] - (_style.padding[__pad1] + _style.padding[__pad2]);
			pSize[__size1] = _baseSize;
			
			_arrow2Holder.x = pSize.width - _arrow2Pos.x - _arrow2Size.width;
			_arrow2Holder.y = pSize.height - _arrow2Pos.y - _arrow2Size.height;
			
			super.size = pSize;
			setScrollProperties(_wSize, _minPos, _maxPos);
		}
		
		override internal function isTabEnabled():Boolean {
			return false;
		}
		
		private function _rollOut(pSender:ButtonSprite, pArgs:MouseEventArgs):void{
			_setView(UIStates.NORMAL, pArgs);
		}
		
		private function _rollOver(pSender:ButtonSprite, pArgs:MouseEventArgs):void{
			_setView(UIStates.HOVER, pArgs);
		}
		
		private function _press(pSender:ButtonSprite, pArgs:MouseEventArgs):void{
			_setView(UIStates.ACTIVE, pArgs);
		}
		
		private function _release(pSender:ButtonSprite, pArgs:MouseEventArgs):void{
			_setView(UIStates.NORMAL, pArgs);
		}
		
		private function _setView(pState:uint, pMe:MouseEventArgs):void {
			var imgSrc:String,
			t:DisplayObjectContainer = pMe.mouseEvent.target as DisplayObjectContainer;

			switch (t){
				case _arrow1Holder:
					imgSrc = "arrow1ImageUrl";
					break;
					
				case _arrow2Holder:
					imgSrc = "arrow2ImageUrl";
					break;
					
				case _thumbHolder:
					imgSrc = "thumbImageUrl";
					break;

				default:
					return;
			}

			_redrawElement(t, pState, imgSrc);
		}
		
		private function _stopScrolling(pSender:ButtonSprite, pArgs:MouseEventArgs):void {
			clearInterval(_scrID);
		}
		
		private function _stopDragThumb(pSender:ButtonSprite, pArgs:MouseEventArgs):void {
			if (_eOnStopDrag) _eOnStopDrag.fire();

			if (_scrolling) {
				UIStage.removeEventListener(MouseEvent.MOUSE_MOVE, _dragThumb);
				_scrolling = false;
			}
		}

		private function _startDragThumb(pSender:ButtonSprite, pArgs:MouseEventArgs):void {
			if (_eOnStartDrag) _eOnStartDrag.fire();
			
			var st:Sprite = _thumbHolder;
			_lastY = st[__mpos1];
			
			if (!_scrolling) {
				UIStage.addEventListener(MouseEvent.MOUSE_MOVE, _dragThumb);
				_scrolling = true;
			}
			
			focus();
		}
		
		private function _dragThumb(pMe:MouseEvent):void {
			var st:Sprite = _thumbHolder;
			var sm:int = (st[__mpos1] - _lastY) + st[__pos2];
//			var size:int = st[__size1];
			
//			var far:int = Math.abs((size >> 2) - st[__mpos2]);
//			if (far > (size << 2)) _stopDragThumb();
			
			if (sm < _minThumbPos) {
				sm = _minThumbPos;
		
			} else if (sm > _maxThumbPos) {
				sm = _maxThumbPos;
			}
			
			
		
			_setScrollPosition(int((_maxPos - _minPos) * ((st[__pos2] = int(sm)) - _minThumbPos) / _tMax) + _minPos);
			pMe.updateAfterEvent();
		}
		
		public function get thumbPosition():int {
			return _thumbHolder[__pos2];
		}
		
		public function set thumbPosition(pPos:int):void {
			if (pPos < _minThumbPos) {
				pPos = _minThumbPos;
		
			} else if (pPos > _maxThumbPos) {
				pPos = _maxThumbPos;
			}
			
			_thumbHolder[__pos2] = pPos;
			_setScrollPosition(int((_maxPos - _minPos) * (pPos - _minThumbPos) / _tMax) + _minPos);
		}
		
		private function _setScrollPosition(pPosition:int):void {
			pPosition = Math.max(Math.min(pPosition, _maxPos), _minPos);
			if (_scrollable){
				if (_position != pPosition){
					_position = pPosition;
					if (_eOnScroll) _eOnScroll.fire();
				}
			}
		}
		
		private function _start1Scroller(pSender:ButtonSprite, pArgs:MouseEventArgs):void {
			_scrollIt(false, -1);
			_scrID = setInterval(_scrollInterval, 500, false, -1);
			focus();
		}
		
		private function _start2Scroller(pSender:ButtonSprite, pArgs:MouseEventArgs):void {
			_scrollIt(false, 1);
			_scrID = setInterval(_scrollInterval, 500, false, 1);
			focus();
		}
		
		private function _scrollIt(pInc:Boolean, pMode:int):void {
			scrollPosition = _position + (pMode * (pInc ? _wSize : _increment));
		}
		
		private function _scrollInterval(pInc:Boolean, pMode:int):void {
			clearInterval(_scrID);
			if (pInc){
				_trackScroller();
				
			} else {
				_scrollIt(pInc, pMode);
			}
			
			_scrID = setInterval(_scrollInterval, 35, pInc, pMode);
		}
		
		private function _trackScroller():void {
			var stb:int = _thumbHolder[__pos2];
			var stk:int = _trackHolder[__mpos1] + _style.padding[__pad1];
			
			if ((stb + _thumbSize) < stk) {
				_scrollIt(true, 1);

			} else if (stk < stb) {
				_scrollIt(true, -1);
			}
		}
		
		private function _startTrackScroller(pSender:ButtonSprite, pArgs:MouseEventArgs):void {
			_trackScroller();
			_scrID = setInterval(_scrollInterval, 500, true, -1);
			focus();
		}
		
		private function _onWheel(pMe:MouseEvent):void {
			_scrollIt(false, -pMe.delta * _increment);
		}
		
		private function _redrawElement(pTarget:DisplayObjectContainer, pState:uint, pEName:String = null):SlicedBitmap {
			if (!_scrollable) pState |= UIStates.DISABLED;

			var bmp:SlicedBitmap = _style.getImage(pState, pEName),
			old:SlicedBitmap, bmp2:SlicedBitmap;
			
			if (bmp){
				if (pTarget.numChildren){
					old = pTarget.getChildAt(0) as SlicedBitmap;
					pTarget.removeChild(old);
				}
				
				bmp2 = (pTarget.addChildAt(bmp, 0)) as SlicedBitmap;
				if (old) {
					bmp2.setSize(old.width, old.height);
					old.dispose();
				}
			}
			
			return bmp2;
		}
		
		public function setScrollProperties(pSize:int, pMinPos:int, pMaxPos:int):void {
			_minPos = Math.max(pMinPos, 0);
			_maxPos = Math.max(pMaxPos, 0);
			_wSize = pSize;
			
			_thumbSize = Math.max(Math.round(pSize / (_maxPos - _minPos + pSize) * _trackSize), _minThumbSize);
			_tMax = _trackSize - _thumbSize;
			
			var off:int = _maxPos - _minPos;
			
			if (off <= 0 || !_enabled){
				_thumbHolder.visible = _scrollable = false;
				
				_trackHandler.enabled = _thumbHandler.enabled = 
				_arrow1Handler.enabled = _arrow2Handler.enabled = false;
				
			} else {
				_trackHandler.enabled = _thumbHandler.enabled = 
				_arrow1Handler.enabled = _arrow2Handler.enabled = true;
				
				_thumbHolder.visible = _scrollable = true;
				_maxThumbPos = _tMax + _minThumbPos;
				
				scrollPosition = _position;
				_redrawElement(_thumbHolder, 1, "thumbImageUrl")[__size2] = _thumbSize;
			}
		
			_redrawElement(_trackHolder, 1)[__size2] = _trackSize;
			_redrawElement(_arrow1Holder, 1, "arrow1ImageUrl");
			_redrawElement(_arrow2Holder, 1, "arrow2ImageUrl");
		}
		
		public function get minPosition():int {
			return _minPos;
		}
		
		public function get maxPosition():int {
			return _maxPos;
		}
		
		public function get scrollPosition():int {
			return _position;
		}
		
		public function set scrollPosition(pPosition:int):void {
			pPosition = Math.max(Math.min(pPosition, _maxPos), _minPos);
			if (_scrollable){
				_thumbHolder[__pos2] = (int((pPosition - _minPos) * _tMax / (_maxPos - _minPos))) + _minThumbPos;
				if (_position != pPosition){
					_position = pPosition;
					if (_eOnScroll) _eOnScroll.fire();
				}
			}
		}
		
		override public function focus():void {
			super.focus();
			
			if (useMouseWheel){
				if (!_mWheeList){
					_mWheeList = true;
					UIStage.addEventListener(MouseEvent.MOUSE_WHEEL, _onWheel);
				}
			}
		}
		
		override public function blur():void {
			super.blur();
			if (_mWheeList){
				_mWheeList = false;
				UIStage.removeEventListener(MouseEvent.MOUSE_WHEEL, _onWheel);
			}
		}
		
		override public function dispose():void {
			if (_disposing) return;
			super.dispose();
			
			setScrollProperties(0, 0, 0);
			
			_trackHandler.dispose();
			_thumbHandler.dispose();
			_arrow1Handler.dispose();
			_arrow2Handler.dispose();
			
			if (_eOnScroll) _eOnScroll.dispose();
			if (_eOnStartDrag) _eOnStartDrag.dispose();
			if (_eOnStopDrag) _eOnStopDrag.dispose();
			
			_disposeBmp(_thumbHolder);
			_disposeBmp(_trackHolder);
			_disposeBmp(_arrow1Holder);
			_disposeBmp(_arrow2Holder);
		}
		
		private function _disposeBmp(pTarget:Sprite):void {
			if (pTarget.numChildren){
				var old:SlicedBitmap = pTarget.getChildAt(0) as SlicedBitmap;
				pTarget.removeChild(old);
				old.dispose();
			}
		}

		override protected function _draw():void {
			setScrollProperties(_wSize, _minPos, _maxPos);
		}
		
		private var _eOnScroll:EventHandler;
		public function get onScroll():EventHandler{
			if (_eOnScroll == null) _eOnScroll = new EventHandler(this);
			return _eOnScroll;
		}
		
		private var _eOnStartDrag:EventHandler;
		public function get onStartDrag():EventHandler{
			if (_eOnStartDrag == null) _eOnStartDrag = new EventHandler(this);
			return _eOnStartDrag;
		}
		
		private var _eOnStopDrag:EventHandler;
		public function get onStopDrag():EventHandler{
			if (_eOnStopDrag == null) _eOnStopDrag = new EventHandler(this);
			return _eOnStopDrag;
		}
	}
}
